package oop.ex2.main;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Conditional {

    private static ArrayList<Variable> mainVariables;
    private static ArrayList<Method> mainMethods;

    /**
     * Constructs a new Conditional object
     * 
     * @param conditionLines
     *            - the array of line of the condition
     * @param mainVariables
     *            - All the declared variables
     * @param mainMethods
     *            - all the declared methods
     * @param currentMethod
     *            - the method which contains this conditional
     * @throws compileException
     * @throws VariableException
     * @throws MethodException
     * @throws ConditionException
     */
    public Conditional(ArrayList<String> conditionLines,
            ArrayList<Variable> mainVariables, ArrayList<Method> mainMethods,
            Method currentMethod) throws compileException, VariableException,
            MethodException, ConditionException {
        // sets the arrays
        this.mainVariables = mainVariables;
        this.mainMethods = mainMethods;
        // checks the condition inside the brackets
        checkConditionValid(conditionLines.get(0), currentMethod);
        conditionLines.remove(0);
        // check each line in the conditional
        for (String line : conditionLines) {

            if (currentMethod != null) {
                checkCondition(line, currentMethod);
            }
            else {
                checkCondition(line, null);
            }
        }

    }

    /**
     * Checks the condition header for the contents inside the brackets
     * 
     * @param line
     *            - the header of the conditional
     * @param currentMethod
     *            - the method that contains the conditional
     * @throws compileException
     * @throws VariableException
     * @throws MethodException
     * @throws ConditionException
     */
    public static void checkCondition(String line, Method currentMethod)
            throws compileException, VariableException, MethodException,
            ConditionException {
        // for a case of a variable
        if (line.matches(Patterns.VARIABLE_DECLARATION)
                || line.matches(Patterns.VARIABLE_SHORT_DECLARATION)) {
            currentMethod.getLocalVariables().add(
                    new Variable(line, mainVariables, mainMethods, null));
        }
        // for a variable assignment
        else if (line.matches(Patterns.VARIABLE_ASSAINMENT)) {
            Sjavac.updateVariable(mainVariables, currentMethod.getVariables(),
                    line);
        }
        // check if the return statement is inside the conditional
        else if (line.matches(Patterns.RETURN_STATEMENT)) {
            currentMethod.matchReturn(line);
        }
        // for a call for a method
        else if (line.matches(Patterns.METHOD_CALL)) {
            Method method = new Method(line, mainMethods, mainVariables, null,
                    null);
        }
        //throw exception for a case of an error
        else {
            if (!line.contains("{") && !line.contains("}")) {
                throw new ConditionException("unknown line: <" + line + ">");

            }
        }
    }

    /**
     * Checks the conditional value inside the brackets for validity
     * 
     * @param line
     *            - the conditional line
     * @param method
     *            - the method that contains the conditional
     * @return true if the conditional is valid
     * @throws compileException
     * @throws ConditionException
     * @throws MethodException
     * @throws VariableException
     */
    private static boolean checkConditionValid(String line, Method method)
            throws compileException, ConditionException, VariableException,
            MethodException {
        // create a pattern and matcher
        Pattern pattern = Pattern.compile(Patterns.IF_WHILE_PATTERN);
        Matcher matcher = pattern.matcher(line);
        if (matcher.matches()) {
            String conditional = matcher.group(2).trim();
            // check id the conditional in case of an assigned value
            if (conditional.matches(VariableType.BOOLEAN.getRegex())) {
                return true;
            }
            // look in the main variables
            if (conditional.matches(Patterns.VARIABLE_NAME)) {
                for (Variable variable : mainVariables) {
                    if (variable.getName().equals(conditional)) {
                        if (variable.getType().matches(
                                VariableType.BOOLEAN.getRegex())) {
                            return true;
                        }
                    }
                }
                // look in the methods local variables
                for (Variable variable : method.getLocalVariables()) {
                    if (variable.getName().trim().equals(conditional.trim())) {
                        if (variable.getType().equals("boolean")
                                && (variable.getValue() != null)) {
                            return true;
                        }
                    }
                }
            }
            // for a case of a method call
            if ((conditional + ";").matches(Patterns.METHOD_CALL)) {
                Pattern patt = Pattern.compile(Patterns.METHOD_CALL);
                Matcher match = patt.matcher(conditional + ";");
                if (match.matches()) {
                    // look in the main methods to match the method call
                    for (Method mainMethod : mainMethods) {
                        if (mainMethod.getName().equals(match.group(1))) {
                            if (mainMethod.getReturnType().equals("boolean")) {
                                Method method2 = new Method(conditional + ";",
                                        mainMethods, mainVariables, null,
                                        method);
                                return true;
                            }
                        }
                    }
                }
            }

        }
        // throw exception in case of an error
        throw new ConditionException(
                "if/while conditional input mismatch in line: <" + line + ">");

    }
}
